package com.mojavelinux.open18;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.persistence.Version;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.hibernate.Hibernate;
import org.hibernate.validator.NotNull;
import org.hibernate.validator.Range;

@Entity
@Table( name = "hole", uniqueConstraints = @UniqueConstraint( columnNames = { "number", "course_id" } ) )
public class Hole implements Serializable, Comparable<Hole> {

	private static final long serialVersionUID = 1L;

	private long id;
	
	private int version = 0;

	private int number;
	
	private Course course;

	private Integer mensPar;

	private Integer mensHandicap;

	private Integer ladiesPar;

	private Integer ladiesHandicap;

	public Hole() {}

	public Hole( int holeNumber ) {
		this( holeNumber, null, null );
	}
	
	public Hole( int holeNumber, Integer mensPar, Integer ladiesPar ) {
		this.number = holeNumber;
		this.mensPar = mensPar;
		if ( ladiesPar == null && mensPar != null ) {
			this.ladiesPar = mensPar;
		}
		else {
			this.ladiesPar = ladiesPar;
		}
	}

	@Id
	@GeneratedValue
	@Column( name = "id", updatable = false )
	public long getId() {
		return this.id;
	}

	public void setId( long id ) {
		this.id = id;
	}

	@Version
	@Column( name = "version" )
	public int getVersion() {
		return version;
	}
	
	protected void setVersion( int version ) {
		this.version = version;
	}

	@Column( name = "number" )
	@NotNull
	@Range( min = 1, max = 18 )
	public int getNumber() {
		return this.number;
	}

	public void setNumber( int number ) {
		this.number = number;
	}

	@ManyToOne( optional = false )
	@NotNull
	public Course getCourse() {
		return course;
	}

	public void setCourse( Course course ) {
		this.course = course;
	}

	/**
	 * The shots required by a first rate golfer to ordinarily negotiate a given hole.
	 * Plays into the handicap calculation for a round, and thus is not an absolute
	 * determinant of whether a player records an "even" score for a round.  There
	 * are actually restrictions based on distance that determines the par of a hole.
	 */
	@Column( name = "m_par" )
	@NotNull
	@Range( min = 2, max = 6 )
	public Integer getMensPar() {
		return mensPar;
	}

	public void setMensPar( Integer mensPar ) {
		this.mensPar = mensPar;
	}

	@Column( name = "m_handicap" )
	//@NotNull
	@Range( min = 1, max = 18 )
	public Integer getMensHandicap() {
		return mensHandicap;
	}

	public void setMensHandicap( Integer mensHandicap ) {
		this.mensHandicap = mensHandicap;
	}

	@Column( name = "l_par" )
	@NotNull
	@Range( min = 2, max = 6 )
	public Integer getLadiesPar() {
		return ladiesPar;
	}

	public void setLadiesPar( Integer ladiesPar ) {
		this.ladiesPar = ladiesPar;
	}

	@Column( name = "l_handicap" )
	//@NotNull
	@Range( min = 1, max = 18 )
	public Integer getLadiesHandicap() {
		return ladiesHandicap;
	}

	public void setLadiesHandicap( Integer ladiesHandicap ) {
		this.ladiesHandicap = ladiesHandicap;
	}
    
    public int compareTo( Hole other ) {
    	return ( number < other.getNumber() ? -1 : ( number == other.getNumber() ? 0 : 1 ) );
    }
    
	/**
	 * Two Hole objects are considered equal if they both have the
	 * same id.  If an id has not been established, then the two
	 * holes must have the same number and belong to the same course.
	 */
    public boolean equals( Object o ) {
        if ( this == o ) {
            return true;
        }

        if ( !( o instanceof Hole ) ) {
            return false;
        }

        final Hole other = (Hole) o;

        // use the id if it is established
        if ( this.id > 0 && other.getId() > 0 ) {
        	return this.id == other.getId();
        }
        
        if ( this.number != other.getNumber() ) {
        	return false;
        }
        
        // Hibernate#isInitialized() return true if not a proxy
        if ( Hibernate.isInitialized( course ) ) {
	        if ( this.course == null ) {
	        	if ( other.getCourse() != null ) {
	        		return false;
	        	}
	        }
	        else if ( !this.course.equals( other.getCourse() ) ) {
	        	return false;
	        }
        }
        
        return true;
    }

    public int hashCode() {
    	final int PRIME = 31;
        int result = 1;
        result = PRIME * result + (int) ( id ^ ( id >>> 32 ) );
        // Hibernate#isInitialized() return true if not a proxy
        if ( Hibernate.isInitialized( course ) ) {
        	result = PRIME * result + ( course != null ? course.hashCode() : 0 );
        }
        
        result = PRIME * result + (int) ( number ^ ( number >>> 32 ) );
    	return result;
    }
	
    public String toString() {
        return new ToStringBuilder( this, ToStringStyle.DEFAULT_STYLE ).
        	append( "id", id ).
            append( "number", number ).
            append( "mensPar", mensPar ).
            append( "mensHandicap", mensHandicap ).
            append( "ladiesPar", ladiesPar ).
            append( "ladiesHandicap", ladiesHandicap ).
            toString();
    }

}
